/*
 * This software is released under the MIT license.
 *
 * Copyright (c) 2017-2023 Code 4 Game, Org. All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is furnished to do
 * so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#include "runtest.h"

#include <string>
#include <sstream>
#include <fstream>
#include <iostream>
#include <filesystem>

#if defined(LIBGLTF_PLATFORM_WINDOWS) && defined(_DEBUG)
#    include <crtdbg.h>
#endif

void SaveAsOBJ(const std::string&                     _sFilePath,
               const libgltf::TVertexList<1, size_t>& _TriangleIndices,
               const libgltf::TVertexList<3, float>&  _VertexPositions,
               const libgltf::TVertexList<2, float>&  _VertexTexcoord,
               const libgltf::TVertexList<3, float>&  _VertexNormal,
               float                                  _Scale = 100.0f)
{
    std::ofstream obj_stream(_sFilePath.c_str());
    if (!obj_stream.good())
        return;

    obj_stream << "# generated by libgltf" << std::endl;

    obj_stream << "# vertex" << std::endl;
    for (size_t i = 0; i < _VertexPositions.size(); ++i)
    {
        const libgltf::TVertexList<3, float>::TValue& position_item = _VertexPositions[i];
        obj_stream << "v " << position_item[0] * _Scale << " " << position_item[1] * _Scale << " " << position_item[2] * _Scale << std::endl;
    }
    obj_stream << std::endl;

    obj_stream << "# texcoord" << std::endl;
    for (size_t i = 0; i < _VertexTexcoord.size(); ++i)
    {
        const libgltf::TVertexList<2, float>::TValue& texcoord_0_item = _VertexTexcoord[i];
        obj_stream << "vt " << texcoord_0_item[0] << " " << texcoord_0_item[1] << std::endl;
    }
    obj_stream << std::endl;

    obj_stream << "# normal" << std::endl;
    for (size_t i = 0; i < _VertexNormal.size(); ++i)
    {
        const libgltf::TVertexList<3, float>::TValue& normal_item = _VertexNormal[i];
        obj_stream << "vn " << normal_item[0] << " " << normal_item[1] << " " << normal_item[2] << std::endl;
    }
    obj_stream << std::endl;

    obj_stream << "# face" << std::endl;
    for (size_t i = 0; i < _TriangleIndices.size(); i += 3)
    {
        obj_stream << "f";
        for (size_t j = 0; j < 3; ++j)
        {
            size_t triangle_item = _TriangleIndices[i + j][0];
            if (triangle_item >= _VertexPositions.size())
            {
                break;
            }
            ++triangle_item;
            obj_stream << " " << triangle_item << "/" << triangle_item << "/" << triangle_item;
        }
        obj_stream << std::endl;
    }
    obj_stream.close();
}

int main(int _argc, char* _argv[])
{
#if defined(LIBGLTF_PLATFORM_WINDOWS) && defined(_DEBUG)
    _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
#endif

#if defined(LIBGLTF_BUILD_COVERAGE)
    int error_code = 0;
#else
    int error_code = 1;
#endif

    std::string input_file_path;
    if (_argc == 2)
    {
        input_file_path = _argv[1];
    }
    if (input_file_path.length() == 0)
    {
        printf("Usage: runtest <file path>\n");
        return error_code;
    }

    std::shared_ptr<libgltf::IglTFLoader> gltf_loader = libgltf::IglTFLoader::Create(
        [input_file_path](const std::string& _path)
        {
            std::filesystem::path file_path;
            if (_path.empty())
                file_path = std::filesystem::path(input_file_path);
            else
                file_path = std::filesystem::path(input_file_path).parent_path().append(_path);

            std::shared_ptr<std::istream> stream_ptr = nullptr;
            if (!std::filesystem::exists(file_path))
                return stream_ptr;

            stream_ptr = std::make_shared<std::ifstream>(file_path.string(), std::ios::in | std::ios::binary);
            return stream_ptr;
        });
    const std::unique_ptr<libgltf::SGlTF>& loaded_gltf = gltf_loader->glTF();
    if (!loaded_gltf)
    {
        printf("Failed\n");
        return error_code;
    }

    libgltf::TVertexList<1, size_t> triangle_data;
    {
        std::shared_ptr<libgltf::TAccessorStream<libgltf::TVertexList<1, size_t>>> accessor_stream =
            std::make_shared<libgltf::TAccessorStream<libgltf::TVertexList<1, size_t>>>(triangle_data);
        gltf_loader->LoadMeshPrimitiveIndicesData(0, 0, accessor_stream);
    }

    libgltf::TVertexList<3, float> position_data;
    {
        std::shared_ptr<libgltf::TAccessorStream<libgltf::TVertexList<3, float>>> accessor_stream =
            std::make_shared<libgltf::TAccessorStream<libgltf::TVertexList<3, float>>>(position_data);
        gltf_loader->LoadMeshPrimitiveAttributeData(0, 0, "position", accessor_stream);
    }

    libgltf::TVertexList<3, float> normal_data;
    {
        std::shared_ptr<libgltf::TAccessorStream<libgltf::TVertexList<3, float>>> accessor_stream =
            std::make_shared<libgltf::TAccessorStream<libgltf::TVertexList<3, float>>>(normal_data);
        gltf_loader->LoadMeshPrimitiveAttributeData(0, 0, "normal", accessor_stream);
    }

    libgltf::TVertexList<2, float> texcoord_0_data;
    {
        std::shared_ptr<libgltf::TAccessorStream<libgltf::TVertexList<2, float>>> accessor_stream =
            std::make_shared<libgltf::TAccessorStream<libgltf::TVertexList<2, float>>>(texcoord_0_data);
        gltf_loader->LoadMeshPrimitiveAttributeData(0, 0, "texcoord_0", accessor_stream);
    }

    std::vector<uint8_t> image0_data;
    std::string          image0_data_type;
    gltf_loader->LoadImageData(0, image0_data, image0_data_type);

    std::string gltf_json;
    if (!(*loaded_gltf >> gltf_json))
        printf("Failed to convert to json!\n");

#if defined(LIBGLTF_BUILD_COVERAGE)
    const std::string obj_file_path = input_file_path + ".obj";
    SaveAsOBJ(obj_file_path, triangle_data, position_data, texcoord_0_data, normal_data);
#endif

    printf("Success\n");
    return error_code;
}
